#pragma once
#include <iostream>
#include <mysql/mysql.h>
#include "json_util.hpp"
namespace leyi_media
{
    namespace media_control
    {
        // Mysql
        const char *const DATABASE = "";
        const char *const HOST = "";
        const char *const USER = "";
        const char *const PASSWD = "";
        // Table info
        const char *const VIDEOS_FIELDS[] =
            {"id", "name", "info", "video_path", "thumb_path", "duration"};
        const short VIDEOS_FIELD_NUM = (sizeof(VIDEOS_FIELDS) / sizeof(VIDEOS_FIELDS[1]));

        class media_controler_t
        {
        private:
            // Singleton pattern
            media_controler_t &operator=(const media_controler_t &) = delete;
            media_controler_t(const media_controler_t &) = delete;
            media_controler_t() : _mysql(mysql_init(nullptr))
            {
                my_bool reconnect = true;
                mysql_options(_mysql, MYSQL_OPT_RECONNECT, &reconnect);
                if (mysql_real_connect(_mysql, HOST, USER, PASSWD, DATABASE, 0, nullptr, 0) == nullptr)
                {
                    std::cerr << "unable to connect mysqld, error:" << mysql_error(_mysql) << std::endl;
                    exit(-1);
                }
                mysql_set_character_set(_mysql, "utf8mb4");
            }
            ~media_controler_t()
            {
                mysql_close(_mysql);
                _mysql = nullptr;
            }

        public:
            // Get instence ref
            static media_controler_t &get_instence()
            {
                // C++ 11
                static media_controler_t ins;
                return ins;
            }

        private:
            // Single query
            bool run_sql(const std::string &sql)
            {
                if (mysql_query(_mysql, sql.c_str()))
                {
                    std::cerr << "run '" << sql << "' failed,error:" << mysql_error(_mysql);
                    return false;
                }
                return true;
            }
            // MYSQL handler
            MYSQL *_mysql;

        public:
            bool insert_video(const Json::Value &root)
            {
                // Parse Json value
                std::string sql("insert into videos (name,info,video_path,thumb_path,duration) values(");
                std::string name(root["name"].asString());
                std::string info(root["info"].asString());
                std::string video(root["video_path"].asString());
                std::string image(root["thumb_path"].asString());
                std::string duration(root["duration"].asString());
                // Structure sql
                sql.reserve(sql.size() + info.size() + 128);
                // Sql e.g ⬇
                // insert into videos (name,info,video,image) values("美羊羊洗澡","高清视频","./balabal","./adsfai", 29 );
                sql += '\"' + std::move(name) + "\"," + '\"' + std::move(info) + "\"," + '\"' + std::move(video) + "\"," + '\"' + std::move(image) + "\"," + ' ' + std::move(duration) + " " + ");";
                // query sql
                return run_sql(sql);
            }
            bool update(const std::string &id, const Json::Value &root)
            {
                std::string sql("update videos set name=");
                std::string info(root["info"].asString());
                std::string name(root["name"].asString());
                sql.reserve(sql.size() + info.size() + name.size() + 128);
                // Sql e.g ⬇
                // update videos set name= "懒洋洋睡觉" info="标清视频" where id=156");
                sql += '\"' + std::move(name) + "\" , " + "info=" + "\"" + std::move(info) + "\" " + "where id=" + std::move(id) + " ;";
                return run_sql(sql);
            }
            bool del(const std::string &id)
            {
                // Delete by primary key
                std::string sql("delete from videos where id= ");
                sql += id + " ;";
                return run_sql(sql);
            }

        public:
            bool select_all(Json::Value &root)
            {
                // Query
                MYSQL_RES *res = nullptr;
                if (!run_sql("select * from videos"))
                {
                    return false;
                }
                // Store
                res = mysql_store_result(_mysql);
                if (res == nullptr)
                {
                    std::cerr << "unable to store query result" << std::endl;
                    return false;
                }
                // Structure Json
                my_ulonglong row_num = mysql_num_rows(res);
                for (my_ulonglong i = 0; i < row_num; i++)
                {
                    MYSQL_ROW row = mysql_fetch_row(res);
                    // Json Array
                    Json::Value elem;
                    for (short j = 0; j < VIDEOS_FIELD_NUM; j++)
                    {
                        elem[VIDEOS_FIELDS[j]] = row[j];
                    }
                    root.append(elem);
                }
                // Free result
                mysql_free_result(res);
                return true;
            };
            bool select_one(const std::string &id, Json::Value &root)
            {
                MYSQL_RES *res = nullptr;
                if (!run_sql(std::move(std::string("select * from videos where id =").append(id))))
                {
                    return false;
                }
                res = mysql_store_result(_mysql);
                if (res == nullptr)
                {
                    std::cerr << "unable to store query result" << std::endl;
                    return false;
                }
                // Structure Json
                my_ulonglong row_num = mysql_num_rows(res);
                if (row_num != 1)
                {
                    std::cout << "unable to find id" << std::endl;
                    mysql_free_result(res);
                    return false;
                }
                MYSQL_ROW row = mysql_fetch_row(res);
                for (short j = 0; j < VIDEOS_FIELD_NUM; j++)
                {
                    root[VIDEOS_FIELDS[j]] = row[j];
                }
                // Free result
                mysql_free_result(res);
                return true;
            }
            bool select_like(const std::string &name, Json::Value &root)
            {
                MYSQL_RES *res = nullptr;
                if (!run_sql(std::string("select * from videos where name like '%").append(name) + "%'"))
                {
                    return false;
                }
                res = mysql_store_result(_mysql);
                if (res == nullptr)
                {
                    std::cerr << "unable to store query result" << std::endl;
                    return false;
                }
                // Structure Json
                my_ulonglong row_num = mysql_num_rows(res);
                for (my_ulonglong i = 0; i < row_num; i++)
                {
                    MYSQL_ROW row = mysql_fetch_row(res);
                    // Json Array
                    Json::Value elem;
                    for (short j = 0; j < VIDEOS_FIELD_NUM; j++)
                    {
                        elem[VIDEOS_FIELDS[j]] = row[j];
                    }
                    root.append(elem);
                }
                // Free result
                mysql_free_result(res);
                return true;
            }
        };
    };
}